package top.jiangqiang.qianyi.common.filter.caching.wrapper;

import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Delegate;
import org.springframework.web.util.WebUtils;

import java.io.*;
import java.nio.charset.Charset;

/**
 * 可缓存的请求包装
 */
public abstract class CachingRWrapper extends HttpServletRequestWrapper {

    /**
     * Constructs a request object wrapping the given request.
     *
     * @param request The request to wrap
     * @throws IllegalArgumentException if the request is null
     */
    public CachingRWrapper(HttpServletRequest request) {
        super(request);
    }

    /**
     * 是否缓存
     *
     * @return
     */
    public abstract boolean isCached();

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (isCached()) {
            return new WrappedServletInputStream(new ByteArrayInputStream(getContentAsByteArray()));
        }
        return super.getInputStream();
    }

    /**
     * 如果没有被缓存，说明数据较大，不应该直接读取到内存，因此抛出异常
     *
     * @return
     */
    public abstract byte[] getContentAsByteArray();

    /**
     * 如果没有被缓存，说明数据较大，不应该直接读取到内存，因此抛出异常
     *
     * @return
     */
    public String getContentAsString() {
        return new String(getContentAsByteArray(), Charset.forName(getCharacterEncoding()));
    }

    @Override
    public String getCharacterEncoding() {
        String enc = super.getCharacterEncoding();
        return (enc != null ? enc : WebUtils.DEFAULT_CHARACTER_ENCODING);
    }

    @Override
    public BufferedReader getReader() throws IOException {
        if (isCached()) {
            return new BufferedReader(new InputStreamReader(getInputStream(), getCharacterEncoding()));
        } else {
            return super.getReader();
        }
    }

    @RequiredArgsConstructor
    private static class WrappedServletInputStream extends ServletInputStream {
        @Delegate
        private final InputStream inputStream;

        @Override
        public boolean isFinished() {
            return true;
        }

        @Override
        public boolean isReady() {
            return true;
        }

        @Override
        public void setReadListener(ReadListener listener) {
            throw new UnsupportedOperationException();
        }

        @Override
        public int read() throws IOException {
            return inputStream.read();
        }
    }
}
